/**
 *
 * \file
 * Timeout handling.
 *
 */

/*
 *
 * Copyright (c) 2010, Jesper Hansen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice, this
 *       list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of the copyright holders nor the names of any contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */

#include <stdint.h>
#include "isendir.h"
#include "timeout.h"


//!
//! Array for storing timeout calls
//!
volatile timeouts_t timeouts[NUM_TIMEOUTS] = {{0}};

//!
//! Global milisecond ticker.
//!
volatile unsigned long g_ticker = 0;

//!
//! Global flags for the timeout handler
//!
volatile unsigned long g_flags = 0;


//*****************************************************************************
//!
//! Set a timeout.
//!	\todo need detailed description
//!	\param 	tmout 		Timeout value in ms (this will be rounded to the nearest larger 10ms step)
//!	\param 	callback 	Pointer to callback function, with will be called when the timeout triggers.
//!	\param 	flag 		The bitpattern to set in \ref g_flags on timeout.
//!	\param 	tmtype 		Type of callback, one-shot, repetitive, see timeout_e enumerated type.
//!	\return 			Timeout index value that can be used to cancel the timeout
//!
//*****************************************************************************
uint32_t timer_set_timeout(uint32_t tmout, PFUNC callback, uint32_t flag, timeout_e tmtype)
{
	uint16_t i;

	tmout /= SYSTICKMS;
	if (tmout == 0)
		tmout = 1;

	// find a free slot
	for (i=0;i<NUM_TIMEOUTS;i++)
	{
		if (timeouts[i].inuse == 0)			// if this is a free slot
		{
			timeouts[i].reload = tmout;		// store the timeout for reloads
			timeouts[i].timeout = tmout;	// setup current timeout
			timeouts[i].pfunc = callback;	// set pointer to callback
			timeouts[i].flag = flag;		// set flag
			timeouts[i].type = tmtype;		// store the type
			timeouts[i].inuse = 1;			// flag the slot as in-use
			return i+1;			  			// return the slot number, 1 based
		}
	}
	return 0;	// no free slots, indicate failure.
}

//*****************************************************************************
//!
//! Cancel a previously set timeout
//!	\param 	tmoutkey 	Value previously retuned from set_timeout
//!
//*****************************************************************************
void timer_cancel_timeout(uint32_t tmoutkey)
{
	if (tmoutkey > 0)
	{
		timeouts[tmoutkey-1].inuse = 0;		// clear the in-use flag
	}
}


//*****************************************************************************
//!
//! Callback for the timeout handler.
//! This function should be called with ulTickMS set to the time since the
//! last call (in ms).
//! \param	ulTickMS	Time since last call (in ms)
//!
//*****************************************************************************
void timeout_tick(uint32_t ulTickMS)
{
    g_ticker += ulTickMS;	// update global with time passed in ms

	// check slots for timeouts
	for (int i=0;i<NUM_TIMEOUTS;i++)
	{
		if (timeouts[i].inuse)						// if this slot is in use
		{
			if (--timeouts[i].timeout == 0)			// if we have a timeout
			{
				if (timeouts[i].pfunc)				// if the callback pointer is valid
					timeouts[i].pfunc();			// call the callback function

				if (timeouts[i].flag != 0)
					g_flags |= timeouts[i].flag;

				if (timeouts[i].type == ONE_SHOT)	// if this was a one time operation
					timeouts[i].inuse = 0;			// clear the in-use flag
				else
					timeouts[i].timeout = timeouts[i].reload;	// otherwise reload for a new round
			}
		}
	}
}



//*****************************************************************************
//!
//! Perform a five cycle delay.
//!
//*****************************************************************************
void __attribute__((naked))
FiveCycleDelay(unsigned long ulCount)
{
    __asm("    subs    r0, #1\n"
    	  "    nop\n"
      	  "    nop\n"
          "    bne     FiveCycleDelay\n"
          "    bx      lr");
}


//*****************************************************************************
//!
//! Delay a number of microseconds.
//!
//! \param	tdelay	Number of microseconds to delay. \note A max of 429 seconds
//!	 				can be used. If larger delays are needed, use delayms instead.
//!
//*****************************************************************************
void delay(uint32_t tdelay)
{
	FiveCycleDelay( tdelay * 10);
}

//*****************************************************************************
//!
//! Delay a number of milliseconds.
//!
//! \param	tdelay	Number of milliseconds to delay. Allow delays up to 49 days.
//!
//*****************************************************************************
void delayms(uint32_t tdelay)
{
	while (tdelay--)
		delay(1000);
}

